﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using AutoStopData.Model;

namespace AutoStopData.BussinessLogic
{
    public static class Trips
    {
        public static void Insert(Trip entity)
        {
            using (var ctx = new AutoStopContext())
            {
                entity.TripId = Guid.NewGuid();
                entity.Created = DateTime.Now;
                entity.Deleted = null;
                ctx.Trips.AddObject(entity);
                ctx.SaveChanges();
            }
        }

        public static void Update(Trip entity)
        {
            using (var ctx = new AutoStopContext())
            {
                var loadedEntity = ctx.Trips.Where(u => u.TripId == entity.TripId).First();
                loadedEntity.InitialCity = entity.InitialCity;
                loadedEntity.DepartureTime = entity.DepartureTime;
                loadedEntity.DepartureInfo = entity.DepartureInfo;
                loadedEntity.DestinationCity = entity.DestinationCity;
                loadedEntity.ArrivalTime = entity.ArrivalTime;
                loadedEntity.ArrivalInfo = entity.ArrivalInfo;
                loadedEntity.SeatCount = entity.SeatCount;
                loadedEntity.SeatPrice = entity.SeatPrice;
                loadedEntity.Note = entity.Note;
                loadedEntity.InitialRegionCode = entity.InitialRegionCode;
                loadedEntity.DestinationRegionCode = entity.DestinationRegionCode;
                ctx.SaveChanges();
            }
        }

        public static void Delete(Trip entity)
        {
            using (var ctx = new AutoStopContext())
            {
                var loadedEntity = ctx.Trips.Where(u => u.TripId == entity.TripId).First();
                loadedEntity.Deleted = DateTime.Now;
                ctx.SaveChanges();
            }
        }

        public static List<Trip> Select(object ownerId, object displayAlsoDeletedRecords, object displayAlsoOldRecords)
        {
            List<Trip> retVal;
            using (var ctx = new AutoStopContext())
            {
                var ownerIdTyped = (Guid)ownerId;
                var displayAlsoDeletedRecordsTyped = (bool)displayAlsoDeletedRecords;
                var displayAlsoOldRecordsTyped = (bool)displayAlsoOldRecords;
                var query = ctx.Trips.OrderByDescending(t => t.Created) as IEnumerable<Trip>;
                
                if (ownerIdTyped != Guid.Empty)
                {
                    query = query.Where(o => o.OwnerId == ownerIdTyped);
                }
                if (!displayAlsoDeletedRecordsTyped) // If displayDeletedRecordsTyped == false
                {
                    query = query.Where(o => o.Deleted == null);
                }
                if (!displayAlsoOldRecordsTyped)
                {
                    query = query.Where(o => o.DepartureTime >= DateTime.Now.AddDays(-2));
                }
                retVal = query.ToList();
            }
            return retVal;
        }

        public static List<Trip> Search(string initialCity, string initialRegionCode, DateTime? departureTime, string destinationCity, string destinationRegionCode, DateTime? arrivalTime)
        {
            List<Trip> retVal;
            using (var ctx = new AutoStopContext())
            {
                var query = ctx.Trips.Where(t => t.Deleted == null);
                if (!string.IsNullOrEmpty(initialRegionCode))
                {
                    query = query.Where(t => t.InitialRegionCode == initialRegionCode);
                }
                if (!string.IsNullOrEmpty(destinationRegionCode))
                {
                    query = query.Where(t => t.DestinationRegionCode == destinationRegionCode);
                }
                query = query.OrderByDescending(t => t.DepartureTime);

                var query2 = from trip in query
                             select new
                             {
                                 trip,
                                 owner =
                                     (
                                         from u in ctx.Users
                                         where u.UserId == trip.OwnerId
                                         select u
                                     ).FirstOrDefault()
                                 ,
                                 ownerRating =
                                     (
                                         from r in ctx.Ratings
                                         join t in ctx.Trips on r.TripId equals t.TripId
                                         where t.OwnerId == trip.OwnerId
                                         group r by true into g
                                         select new
                                         {
                                             RatingAverage = g.Average(o => o.RatingNum),
                                             RatingCount = g.Count()
                                         }
                                     ).FirstOrDefault(),
                                 ratings =
                                     (
                                         from r in ctx.Ratings
                                         where r.TripId == trip.TripId
                                         select r
                                     )
                             };
                retVal = new List<Trip>();
                foreach (var result in query2.ToList())
                {
                    if (result.ownerRating != null)
                    {
                        result.owner.RatingAverage = result.ownerRating.RatingAverage;
                        result.owner.RatingCount = result.ownerRating.RatingCount;
                    }
                    retVal.Add(result.trip);
                }

                //retVal = query.ToList();
                if (departureTime != null)
                {
                    retVal = retVal.Where(t => t.DepartureTime != null && t.DepartureTime.Value.Date == departureTime.Value).ToList();
                }
                if (arrivalTime != null)
                {
                    retVal = retVal.Where(t => t.ArrivalTime != null && t.ArrivalTime.Value.Date == arrivalTime.Value).ToList();
                }
                if (!string.IsNullOrEmpty(Normalize(initialCity)))
                {
                    retVal = retVal.Where(t => Normalize(t.InitialCity).Contains(Normalize(initialCity))).ToList();
                }
                if (!string.IsNullOrEmpty(Normalize(destinationCity)))
                {
                    retVal = retVal.Where(t => Normalize(t.DestinationCity).Contains(Normalize(destinationCity))).ToList();
                }
            }
            return retVal;
        }

        public static List<Trip> GetRecordsBeforeDeparture(int daysCount)
        {
            var retVal = Search(String.Empty, String.Empty, null, String.Empty, String.Empty, null);
            var today = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
            var dateTo = today.AddDays(daysCount + 1).AddSeconds(-1);
            retVal = retVal.Where(t => t.DepartureTime != null && t.DepartureTime.Value.Date >= today && t.DepartureTime.Value.Date <= dateTo).ToList();
            return retVal;
        }

        public static String Normalize(string input)
        {
            String retVal = String.Empty;
            string normalizedString = input.Normalize(NormalizationForm.FormD);
            return normalizedString.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark && char.IsLetter(c)).Aggregate(retVal, (current, c) => current + char.ToUpper(c));
        }

        public static bool UserCanRateTrip(IEnumerable<Rating> ratings, Guid? currentUserId)
        {
            bool canRate = true;
            if (currentUserId == null)
            {
                canRate = false;
            }
            else
            {
                if (ratings.Count() > 0)
                {
                    canRate = !ratings.Any(r => r.EvaluatorId == currentUserId);
                }
            }
            return canRate;
        }

        public static void AddRating(Guid tripId, Guid evaluatorId, decimal value)
        {
            using (var ctx = new AutoStopContext())
            {
                if (!ctx.Ratings.Any(r => r.TripId == tripId && r.EvaluatorId == evaluatorId))
                {
                    var rating = new Rating
                    {
                        TripId = tripId,
                        EvaluatorId = evaluatorId,
                        RatingNum = value,
                        RatedDate = DateTime.Now
                    };
                    ctx.Ratings.AddObject(rating);
                    ctx.SaveChanges();
                }
            }
        }
    }
}
